package com.usbconnecter;

import android.app.Activity;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ProviderInfo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.hardware.usb.UsbConfiguration;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
import android.mtp.MtpConstants;
import android.mtp.MtpDevice;
import android.mtp.MtpEvent;
import android.mtp.MtpObjectInfo;
import android.util.Log;
import android.widget.ImageView;
import android.widget.Toast;

import com.fileoperation.BaseSetting;
import com.fileoperation.FileTools;
import com.usbconnecter.eos.EosEvent;
import com.usbconnecter.eos.EosInitiator;
import com.usbconnecter.nikon.NikonInitiator;

import org.greenrobot.eventbus.EventBus;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import rx.Observable;
import rx.Subscriber;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Action1;
import rx.schedulers.Schedulers;

/**
 * Created by Administrator on 2016-12-05.
 */

public class UsbConnector {
    private UsbDevice mUsbDevice;
    private UsbManager mUsbManager;
    private UsbDeviceConnection mDeviceConnection;
    private UsbInterface mInterface;
    private Activity activity;
    private boolean isConnected = false;
    private MtpDevice mtpDevice;
    private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";
    private static final String ACTION_USB_ATTACHED = "android.hardware.usb.action.USB_DEVICE_ATTACHED";
    private MtpInfoInterface mtpInfoInterface;
    private UsbEndpoint usbEndpointIn;
    private UsbEndpoint usbEndpointOut;
    private UsbEndpoint usbEndpointInterrupt;
    private UsbEndpoint usbEndpointControl;


    private BaselineInitiator bi;

    private static final String TAG = "UsbConnector";
    private boolean isCanon = false;
    private boolean isNikon = false;


    private BroadcastReceiver usbReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();

            if (action.equals(ACTION_USB_ATTACHED)) {
                PendingIntent mPermissionIntent = PendingIntent.getBroadcast(context, 0, new Intent(ACTION_USB_PERMISSION), 0);
                mUsbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
                if (mUsbDevice != null) {
                    ((UsbManager) context.getSystemService(Context.USB_SERVICE)).requestPermission(mUsbDevice, mPermissionIntent);
                }
            } else if (ACTION_USB_PERMISSION.equals(action)) {
                synchronized (this) {
                    mtpInfoInterface.onStart();
                    if (mUsbDevice == null) {
                        mtpInfoInterface.onContentListReady(null);
                        return;
                    }
                    connectDevice();
                }
            } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
                Log.e(TAG, "CAMERA DETTACHED");
                BaseSetting.getInstance().setUsbAvailable(false);
                releaseConnect();
            }
        }
    };


    public UsbConnector(Activity activity, MtpInfoInterface mtpInfoInterface) {
        this.mtpInfoInterface = mtpInfoInterface;
        this.activity = activity;
    }

    /**
     * 开始扫描
     */
    public void startScan() {
        mtpInfoInterface.onStart();
        IntentFilter filter = new IntentFilter();
        filter.addAction(ACTION_USB_ATTACHED);
        filter.addAction(ACTION_USB_PERMISSION);
        filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
        filter.setPriority(Integer.MAX_VALUE);
        activity.registerReceiver(usbReceiver, filter);

        mUsbManager = (UsbManager) activity.getSystemService(Context.USB_SERVICE);

        HashMap<String, UsbDevice> deviceList = mUsbManager.getDeviceList();
        if (deviceList.isEmpty()) {
            mtpInfoInterface.onContentListReady(null);
            return;
        } else {
            mUsbDevice = deviceList.values().iterator().next();
        }

        if (mUsbDevice == null) {
            mtpInfoInterface.onContentListReady(null);
            return;
        }
        ;
        PendingIntent mPermissionIntent = PendingIntent.getBroadcast(activity, 0, new Intent(ACTION_USB_PERMISSION), 0);
        ((UsbManager) activity.getSystemService(Context.USB_SERVICE)).requestPermission(mUsbDevice, mPermissionIntent);

    }

    public void deleteObject(final List<MtpObjectInfo> list) {
        if (!isConnected || mtpDevice == null) {
            return;
        }
        Observable.create(new Observable.OnSubscribe<Integer>() {
            @Override
            public void call(Subscriber<? super Integer> subscriber) {
                for (int i = 0; i < list.size(); i++) {
                    if (mtpDevice.deleteObject(list.get(i).getObjectHandle())) {
                        subscriber.onNext(i);
                    }
                }
                subscriber.onCompleted();
            }
        })
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<Integer>() {
                    @Override
                    public void onCompleted() {
                        mtpInfoInterface.onDeleteComplete();
                        Toast.makeText(activity, "删除成功！", Toast.LENGTH_SHORT).show();

                    }

                    @Override
                    public void onError(Throwable e) {
                        Toast.makeText(activity, "删除照片失败！", Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onNext(Integer integer) {
                        mtpInfoInterface.onDeleteProgress(integer, list.size());
                    }
                });
    }

    private void connectDevice() {
        Observable.create(new Observable.OnSubscribe<ArrayList<MtpObjectInfo>>() {
            @Override
            public void call(Subscriber<? super ArrayList<MtpObjectInfo>> subscriber) {

                initDevice(mUsbDevice);

                subscriber.onNext(null);
                subscriber.unsubscribe();
            }
        })
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action1<ArrayList<MtpObjectInfo>>() {
                    @Override
                    public void call(ArrayList<MtpObjectInfo> list) {
                        System.out.println("***************************");
                        mtpInfoInterface.onContentListReady(list);
//                        readEvent();
                        BaseSetting.getInstance().setUsbAvailable(true);
                    }
                });
    }


    /**
     * opens session, sends EOS_OC_Capture command, closes session
     *
     * @param session
     * @return
     */
    public boolean releaseShutter(Session session) {
        Log.d(TAG, "Starting releaseShutter");
        boolean result = session.isActive();
        if (bi.device == null) {
            Log.w(TAG, "NO DEVICE OPENED");
            return false;
        }
        if (!bi.isSessionActive())
            try {
                bi.openSession();
            } catch (PTPException e) {
                e.printStackTrace();
                result = false;
            }
        if (bi.isSessionActive()) {
            try {
                Log.d(TAG, "Start initiateCapture");
                bi.initiateCapture(0, 0);
                result = true;
            } catch (PTPException e) {
                e.printStackTrace();
                result = false;
            }
        }
        return result;
    }  //releaseShutter


    private boolean isMonitoringEvent = false;



    private ArrayList<MtpObjectInfo> pullObjectList() {
        if (!isConnected) return null;
        int[] ids = mtpDevice.getStorageIds();
        if (ids == null) {
            return null;
        }
        return getObjectList(mtpDevice, ids[0], 0);
    }




    private boolean isReadingIn = false;





    public void onDestroy() {
        detachDevice();
        if (this.mDeviceConnection != null) {
            if (this.mInterface != null) {
                this.mDeviceConnection.releaseInterface(this.mInterface);
                this.mInterface = null;
            }
            this.mDeviceConnection.close();
            this.mUsbDevice = null;
            this.mInterface = null;
        }
        if (mtpDevice != null) {
            mtpDevice.close();
        }
        isReadingIn = false;
        isConnected = false;
        isMonitoringEvent = false;
        activity.unregisterReceiver(usbReceiver);
        activity = null;
    }

    private void releaseConnect() {
        detachDevice();
        if (this.mDeviceConnection != null) {
            if (this.mInterface != null) {
                this.mDeviceConnection.releaseInterface(this.mInterface);
                this.mInterface = null;
            }
            this.mDeviceConnection.close();
            this.mUsbDevice = null;
            this.mInterface = null;
        }
        if (mtpDevice != null) {
            mtpDevice.close();
        }
        isReadingIn = false;
        isConnected = false;
        isMonitoringEvent = false;
    }

    public void showThumbnail(final ImageView imageView, final MtpObjectInfo mtpObjectInfo) {
        Log.w("获取thumbnail", "开始获取缩略图");
        if (mtpDevice == null) return;
        Observable.create(new Observable.OnSubscribe<Bitmap>() {
            @Override
            public void call(Subscriber<? super Bitmap> subscriber) {
                byte[] rawObject = mtpDevice.getThumbnail(mtpObjectInfo.getObjectHandle());
                if (rawObject == null) {
                    subscriber.unsubscribe();
                    return;
                }
                Bitmap bitmap = null;
                bitmap = BitmapFactory.decodeByteArray(rawObject, 0, rawObject.length);
                System.out.println("---------------raw data------------------" + rawObject);
                subscriber.onNext(bitmap);
                subscriber.unsubscribe();
            }
        })
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<Bitmap>() {
                    @Override
                    public void onCompleted() {
                        Log.w("获取thumbnail", "成功");
                    }

                    @Override
                    public void onError(Throwable e) {
                        Log.w("获取thumbnail", "error");
                    }

                    @Override
                    public void onNext(Bitmap bitmap) {
                        imageView.setImageBitmap(bitmap);
                    }
                });

    }


    private ArrayList<MtpObjectInfo> getObjectList(MtpDevice device, int storageId, int objectHandle) {
        System.out.println("******************开始获取object********************");
        int[] handles = device.getObjectHandles(storageId, 0, objectHandle);
        if (handles == null) {
            System.out.println("***************handles = null********************");
            return null;
        }
        int length = handles.length;
        ArrayList<MtpObjectInfo> objectList = new ArrayList<>(length);
        for (int handle : handles) {
            MtpObjectInfo info = device.getObjectInfo(handle);
            if (info == null) {
                Log.w("object info", "getObjectInfo failed");
            } else if (checkFileTypeValidation(info.getFormat()) && info.getThumbCompressedSize() != 0) {
                Log.w("object info", "name=" + info.getName() + " AssociationType=" + info.getAssociationType() + " format=" + info.getFormat());
                objectList.add(info);
            } else {
                Log.w("object info", "格式不支持");
            }
        }
        Log.w("object size", objectList.size() + "");

        return objectList;
    }


    private boolean checkFileTypeValidation(int format) {
        return format == MtpConstants.FORMAT_EXIF_JPEG ||
                format == 45315 || //CR2格式文件
                format == MtpConstants.FORMAT_PNG ||
                format == MtpConstants.FORMAT_TIFF_EP ||
                format == MtpConstants.FORMAT_BMP ||
                format == MtpConstants.FORMAT_GIF ||
                format == MtpConstants.FORMAT_JFIF ||
                format == MtpConstants.FORMAT_PICT ||
                format == MtpConstants.FORMAT_TIFF ||
                format == MtpConstants.FORMAT_JP2 ||
                format == MtpConstants.FORMAT_JPX ||
                format == MtpConstants.FORMAT_DNG ||
                format == MtpConstants.FORMAT_UNDEFINED;
    }


    /**
     * 保存图片到本地
     * onNext的参数是一个int类型，最低位代表是否保存成功（1代表成功，0代表失败），1位之后的数字代表图片的序号
     *
     * @param list
     */
    public void saveImageLocal(final List<MtpObjectInfo> list) {
        if (!isConnected || mtpDevice == null) return;

        Observable.create(new Observable.OnSubscribe<Integer>() {
            @Override
            public void call(Subscriber<? super Integer> subscriber) {
                for (int i = 0; i < list.size(); i++) {
                    final MtpObjectInfo mtpObjectInfo = list.get(i);
                    final String path = FileTools.DIR_PATH + File.separator + mtpObjectInfo.getName();
                    File file = new File(path);
                    if (file.exists()) {
                        subscriber.onNext((i << 1) + ConstantsUsb.SAVE_SUCCESS);
                        continue;
                    } else {
                        try {
                            file.createNewFile();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    int objectHandle = mtpObjectInfo.getObjectHandle();
                    FileOutputStream fileOutputStream = null;
                    BufferedOutputStream bufferedOutputStream = null;
                    try {
                        fileOutputStream = new FileOutputStream(path);
                        bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
                        byte[] bitmap = mtpDevice.getObject(objectHandle, mtpObjectInfo.getCompressedSize());
                        if (bitmap != null) {
                            bufferedOutputStream.write(bitmap);
                            subscriber.onNext((i << 1) + ConstantsUsb.SAVE_SUCCESS);
                        } else {
                            subscriber.onNext((i << 1) + ConstantsUsb.SAVE_ERROR);
                            file.delete();
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                        subscriber.onNext((i << 1) + ConstantsUsb.SAVE_ERROR);
                        file.delete();
                    } finally {
                        // 关闭创建的流对象
                        if (fileOutputStream != null) {
                            try {
                                fileOutputStream.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                        if (bufferedOutputStream != null) {
                            try {
                                bufferedOutputStream.close();
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
                subscriber.onCompleted();
                subscriber.unsubscribe();
            }
        })
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<Integer>() {
                    @Override
                    public void onCompleted() {
                        Toast.makeText(activity, "传输文件完成:" + FileTools.DIR_PATH, Toast.LENGTH_SHORT).show();
                        mtpInfoInterface.onSaveCompleted();
                    }

                    @Override
                    public void onError(Throwable e) {
                        Toast.makeText(activity, "传输文件失败！", Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onNext(Integer integer) {
                        boolean success = (integer & 1) == ConstantsUsb.SAVE_SUCCESS;
                        int index = integer >> 1;
                        mtpInfoInterface.onSaveProgress(index, list.size(), success);
                    }
                });
    }


    // inits device,
    public void initDevice(UsbDevice device) {
        try {
            if (device != null) {
                Log.d(TAG, "initDevice: " + device.getDeviceName());
                //	        	log ("Device: " +device.getDeviceName());

                bi = InitiatorFactory.getInitiator(device, mUsbManager.openDevice(device));
                bi.openSession();
                bi.setEosEventMode();//佳能相机监听事件
                bi.startListenEndpointInterrupt();
            }
        } catch (PTPException e) {
            e.printStackTrace();
            BaseSetting.getInstance().setUsbAvailable(false);
        }

    }

    public void detachDevice() {
        if (bi != null /*&& mDevice.equals(device)*/) {
            if (bi.device != null) Log.d(TAG, "detachDevice: " + bi.device.getDeviceName());
            try {
//                bi.getClearStatus();
                bi.close();
                bi = null;
            } catch (PTPException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }


}
